;*******************************************************************************
; Director - Menus
;
; Copyright (C) 2003, Nick Craig-Wood and Philip Ludlam
;
;This program is free software; you can redistribute it and/or modify it under
;the terms of the GNU General Public License as published by the Free Software
;Foundation; either version 2 of the License, or (at your option) any later
;version.
;
;This program is distributed in the hope that it will be useful, but WITHOUT ANY
;WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
;PARTICULAR PURPOSE. See the GNU General Public License for more details.
;
;You should have received a copy of the GNU General Public License along with
;this program; if not, write to the Free Software Foundation, Inc., 59 Temple
;Place - Suite 330, Boston, MA 02111-1307, USA
;
;*******************************************************************************
;----h- Director.s.Menus
; Name
;   Menus
;
; Purpose
;   Menus
;------
;*******************************************************************************


		TTL	> Menus

		GET	OSLib:oslib.hdr.OSFile
		GET	OSLib:oslib.hdr.OSFSControl
		GET	OSLib:oslib.hdr.OSGBPB
		GET	OSLib:oslib.hdr.Wimp
		GET	OSLib:oslib.hdr.WimpSpriteOp
		GET	AsmLib2:hdr.RegsBoth
		GET	AsmLib2:hdr.MacrosBoth
		GET	h.WorkSpace
		GET	h.ListMacros
		GET	h.Constants
		GET	h.Memory
		GET	h.Messages
		GBLL	|s.menus|
		GET	h.Menus
		GET	h.Sort

		AREA	|Menus|, CODE, READONLY

string_marker	EQU	&01


;*******************************************************************************
;----f- Director.s.Menus.ClaimMediaUpCalls
; Name
;   ClaimMediaUpCalls
;
; Purpose
;   This claims media changed / not found up calls
;
; Entry
;   none
;
; Exit
;   none
;------
;*******************************************************************************


ClaimMediaUpCalls	ROUTINE "r0-r3", EXPORT

		MOV	r0, #UpCallV
		ADR	r1, UpCallHandler
		MOV	r2, wp
		SWI	XOS_Claim

		EXIT


;*******************************************************************************
;----f- Director.s.Menus.ReleaseMediaUpCalls
; Name
;   ReleaseMediaUpCalls
;
; Purpose
;   This releases media changed / not found up calls
;
; Entry
;   none
;
; Exit
;   none
;------
;*******************************************************************************


ReleaseMediaUpCalls	ROUTINE "r0-r3", EXPORT

		MOV	r0, #UpCallV
		ADR	r1, UpCallHandler
		MOV	r2, wp
		SWI	XOS_Release

		EXIT


;*******************************************************************************
;----f- Director.s.Menus.UpCallHandler
; Name
;   UpCallHandler
;
; Purpose
;   This is the UpCall handler
;   All it does is intercept media not known and media not present
;
; Entry
;
;
; Exit
;
;------
;*******************************************************************************


UpCallHandler	TEQ	r0, #UpCall_MediaNotPresent	; 0, Media not present
		TEQNE	r0, #UpCall_MediaNotKnown	; 1, Media not known
		MOVNE	pc, lr				; pass on the UpCall

		MOV	r0, #-1				; cancel the operation
		LDMFD	sp!, {pc}			; get intercept lr from the stack


;*******************************************************************************
;----f- Director.s.Menus.PathParse
; Name
;   PathParse
;
; Purpose
;   This parses a path string into two parts
;   Works on control terminated strings
;
; Entry
;   r0  path string
;
; Exit
;   r0  leaf name
;   EQ if string didn't parse into 2 bits, NE if it did
;------
;*******************************************************************************


PathParse	ROUTINE	"r1-r3", EXPORT

		MOV	r1, r0				; r1 points to the start
		BL	strlen				; find actual length
		CMP	r0, #0
		BEQ	not_found$l			; end if zero length string

		ADD	r2, r1, r0			; r2 points to the terminator

		LDRB	r0, [r2, #-1]
		CMP	r0, #':'
		BEQ	not_found$l			; ':' at end is just a path/fs

strip_dot$l
		LDRB	r0, [r2, #-1]!
		CMP	r0, #':'
		LDREQB	r3, [r2, #-2]
		CMPEQ	r3, #':'				; is it similar to adfs::HardDisk4 ?
		BEQ	not_found$l			; if so it can't be split

		CMP	r0, #'.'
		CMPNE	r0, #':'				; as in Files:Programs
		BEQ	dot_found$l

		CMP	r0, #'$'
		CMPNE	r0, #'&'
		CMPNE	r0, #'@'
		CMPNE	r0, #'%'
		BEQ	not_found$l

		CMP	r2, r1
		BGT	strip_dot$l			; strip the directory name off the path

not_found$l	MOV	r0, r1				; point to start of string
		SetZ
		EXIT

dot_found$l	ADD	r0, r2, #1			; point to leaf
		ClearZ
		EXIT


;*******************************************************************************
;----f- Director.s.Menus.IfFileMakeUp
; Name
;   IfFileMakeUp
;
; Purpose
;   If the path supplied points to a file then it is changed to point to a
;directory.
;   It may return an error
;
; Entry
;   r0  path name
;
; Exit
;   path name is altered
;------
;*******************************************************************************


IfFileMakeUp	ROUTINE	"r0-r3, r8", EXPORT

		MOV	r8, r0				; r8  path

		MOV	r0, #OSFile_ReadNoPath
		MOV	r1, r8
		SWI	XOS_File
		BVS	error$l
;;;;		CMP	r0, #OSFile_NotFound		; was the file found?
		CMP	r0, #OSFile_IsDir
		CMPNE	r0, #OSFile_IsImage
		BEQ	ok$l				; ok if path points to directory

		MOV	r0, r8				; strip leaf name off
		BL	PathParse
		BEQ	ok$l				; no dot is ok
		LDRB	lr, [r0, #-1]			; what was terminator?
		CMP	lr, #"."
		MOV	lr, #0
		STREQB	lr, [r0, #-1]			; 0 terminate path (over write '.')
		STRNEB	lr, [r0]				; 0 terminate path (write past ':')

ok$l		ClearV
		EXIT

error$l		EXIT_ERR


;*******************************************************************************
;----f- Director.s.Menus.SetVarVal
; Name
;   SetVarVal
;
; Purpose
;   This sets a system variable
;
; Entry
;   r0  system variable name
;   r1  string for system variable
;   r4 = system variable type
;
; Exit
;   If an error occurred:
;     r0 = pointer to valid error block
;     VS
;   Otherwise
;     VC
;------
;*******************************************************************************


SetVarVal	ROUTINE	"r0-r4", EXPORT

		MOV	r3, r0				; r3  name
		MOV	r0, r1
		BL	strlen
		MOV	r2, r0				; r2 = length of string
		MOV	r0, r3				; r0  name
		MOV	r3, #0				; context
		SWI	XOS_SetVarVal			; set the system variable

		STRSP	r0, r0, VS			; if an error occurred put the current value of r0 on the stack
		EXIT


;*******************************************************************************
;----f- Director.s.Menus.SetVarValN
; Name
;   SetVarValN
;
; Purpose
;   This sets a system variable to a number
;
; Entry
;   r0  system variable name
;   r1 = value for system variable
;
; Exit
;   If an error occurred:
;     r0 = pointer to valid error block
;     VS
;   Otherwise
;     VC
;------
;*******************************************************************************


SetVarValN	ROUTINE	"r0-r4", EXPORT

		ADRSP	r1, r1				; r1  value for system variable
		MOV	r2, #4				; r2 = length of string
		MOV	r3, #0				; context
		MOV	r4, #OS_VartypeNumber		; a number
		SWI	XOS_SetVarVal			; set the system variable

		STRSP	r0, r0, VS			; if an error occurred put the current value of r0 on the stack
		EXIT


;*******************************************************************************
;----f- Director.s.Menus.UnsetVarVal
; Name
;   UnsetVarVal
;
; Purpose
;   This unsets a system variable
;
; Entry
;   r0  system variable name
;
; Exit
;   If an error occurred:
;     VS
;   Otherwise
;     VC
;   It is presumed that the calling routine does not care whether
;   an error occurs or not.
;------
;*******************************************************************************


UnsetVarVal	ROUTINE "r0-r4", EXPORT

							; r0  var name
		MOV	r1, #-1				; r1  value
		MOV	r2, #-1				; delete variable
		MOV	r3, #0				; first call
		MOV	r4, #OS_VartypeString		; r4 = 0
		SWI	XOS_SetVarVal

;		STRSP	r0, r0, VS			; if an error occurred put the current value of r0 on the stack
		EXIT


;*******************************************************************************
;----f- Director.s.Menus.SetCurrentPath
; Name
;   SetCurrentPath
;
; Purpose
;   This GSTrans-es the string pointed to by r0 and sets it to
;   CurrentPath.  It then parses the string into CurrentLeaf and CurrentDir
;
; Entry
;   r0  string
;
; Exit
;   It may return errors
;------
;*******************************************************************************


SetCurrentPath	ROUTINE_SF	"r0-r7", EXPORT
string$l	#	StringSize
		END_SF

		ADR	r1, string$l
		MOV	r2, #StringSize
		SWI	XOS_GSTrans
		BVS	error$l
		BCS	toosmall$l

		ADR	r0, CurrentPath			; r0  name
		ADR	r1, string$l			; r1  value
		MOV	r4, #OS_VartypeLiteralString
		BL	SetVarVal			; set the system variable
		BVS	error$l

		ADR	r0, string$l
		BL	PathParse			; r0  leaf
		MOVEQ	r6, #0
		BEQ	noterm$l			; r6 = 0 => don't terminate

		LDRB	lr, [r0, #-1]			; what was terminator?
		CMP	lr, #'.'			; r6  terminator
		SUBEQ	r6, r0, #1			; 0 terminate path (over write '.')
		MOVNE	r6, r0				; 0 terminate path (write past ':')
noterm$l
		MOV	r1, r0				; r1  leaf
		ADR	r0, CurrentLeaf			; r0  name
		MOV	r4, #OS_VartypeLiteralString
		BL	SetVarVal			; set the system variable
		BVS	error$l

		TEQ	r6, #0
		LDRNEB	r5, [r6]			; r5 = contents of terminator location
		MOVNE	lr, #0				; write a null over the '.'
		STRNEB	lr, [r6]

		ADR	r0, CurrentDir			; r0  name
		ADR	r1, string$l			; r1  value
		MOV	r4, #OS_VartypeLiteralString
		BL	SetVarVal			; set the system variable
		TEQ	r6, #0
		STRNEB	r5, [r6]			; restore the terminator
		BVS	error$l

		EXIT

toosmall$l	ADR	r0, toosmallmsg$l
		BL	MessageErrorLookup

error$l		EXIT_ERR

toosmallmsg$l	ERROR	"$BufferTooSmall", Error_BufferTooSmall

CurrentPath	EQUS0A	"$Name.$$CurrentPath"
CurrentLeaf	EQUS0A	"$Name.$$CurrentLeaf"
CurrentDir	EQUS0A	"$Name.$$CurrentDir"
		ALIGN


;*******************************************************************************
;----f- Director.s.Menus.LookUpKey
; Name
;   LookUpKey
;
; Purpose
;   This converts a key string into a key definition
;   It may return an error
;
; Entry
;   r0  string with key definition
;
; Exit
;   If an error occurred:
;     r0 = pointer to valid error block
;     VS
;   Otherwise
;     r0 = key number
;     VC
;------
;*******************************************************************************


LookUpKey	ROUTINE	"r1-r9", EXPORT

		MOV	r8, r0				; r8  key string not processed
		MOV	r9, #0				; r9 = modifiers

modifier$l	LDRB	r0, [r8]
		CMP	r0, #''
		ORREQ	r9, r9, #ShiftMod
		ADDEQ	r8, r8, #1			; skip modifier
		BEQ	modifier$l
		CMP	r0, #'^'
		ORREQ	r9, r9, #CtrlMod
		ADDEQ	r8, r8, #1			; skip modifier
		BEQ	modifier$l
		CMP	r0, #'~'
		ORREQ	r9, r9, #AltMod
		ADDEQ	r8, r8, #1			; skip modifier
		BEQ	modifier$l

		MOV	r0, r8
		BL	strlen

		CMP	r0, #1
		BLT	notfound$l
		BGT	string$l
		LDRB	r0, [r8]
		UpperCase	r0, lr
		B	found$l

string$l	ADR	r1, keys$l			; r1 points to table of "name", 0, msb, lsb entries

search$l	MOV	r0, r8
		BL	strcmpi
		BEQ	found1$l

		MOV	r0, r1
		BL	strlen1
		ADD	r0, r0, #2			; and 2 bytes
		ADD	r1, r1, r0			; r1  next block

		LDRB	r0, [r1]
		CMP	r0, #0
		BNE	search$l			; keep searching until end

notfound$l	ADR	r0, notfoundmsg$l
		BL	MessageErrorLookup
		SetV
		EXIT

notfoundmsg$l	ERROR	"$KeyNotFound", Error_KeyNotFound

found1$l	MOV	r0, r1
		BL	strlen1
		ADD	r1, r1, r0

		LDRB	r0, [r1, #0]			; lsb
		LDRB	r1, [r1, #1]			; msb
		ORR	r0, r0, r1, LSL#8		; key number found

found$l		CMP	r0, #&180
		BLT	notwimp$l
		CMP	r0, #&200
		BGE	notwimp$l
		TST	r9, #ShiftMod
		EORNE	r0, r0, #&10
		TST	r9, #CtrlMod
		EORNE	r0, r0, #&20
		B	ok$l
notwimp$l
		CMP	r0, #64
		BLT	notalpha$l
		CMP	r0, #64 + 32
		BGE	notalpha$l
		TST	r9, #CtrlMod
		SUBNE	r0, r0, #64
;		B	ok$l
notalpha$l

ok$l		ORR	r0, r0, r9
		ClearV
		EXIT

;Key definitions

		MACRO
$label		AKey	$name, $number
$label
		DCB	"$name", 0
		DCB	$number :AND: &FF
		DCB	($number >> 8) :AND: &FF
		MEND

keys$l		AKey	Enter,		&00D
		AKey	Return,		&00D
		AKey	Ret,		&00D
		AKey	Home,		&01E
		AKey	Backspace,	&01C
		AKey	BkSp,		&01C
		AKey	Space,		&020
		AKey	Esc,		&01B
		AKey	Delete,		&07F
		AKey	Del,		&07F
		AKey	Print,		&180
		AKey	Pnt,		&180
		AKey	F0,		&180
		AKey	F1,		&181
		AKey	F2,		&182
		AKey	F3,		&183
		AKey	F4,		&184
		AKey	F5,		&185
		AKey	F6,		&186
		AKey	F7,		&187
		AKey	F8,		&188
		AKey	F9,		&189
		AKey	Tab,		&18A
		AKey	Copy,		&18B
		AKey	Cpy,		&18B
		AKey	End,		&18B
		AKey	Left, 		&18C
		AKey	Right,		&18D
		AKey	Down,		&18E
		AKey	Up,		&18F
		AKey	PageDown,	&19E
		AKey	PgDn,		&19E
		AKey	PageUp,		&19F
		AKey	PgUp,		&19F
		AKey	WinLogo,	&1C0
		AKey	WinMenu,	&1C1
		AKey	F10,		&1CA
		AKey	F11,		&1CB
		AKey	F12,		&1CC
		AKey	Insert,		&1CD
		AKey	Ins,		&1CD
		;
		DCB	0
		ALIGN


;*******************************************************************************
;----f- Director.s.Menus.TextAndKey
; Name
;   TextAndKey
;
; Purpose
;   This looks up a key string into a key number and
;   writes the hotkey into the KeyBlock if found
;   It may return an error
;
; Entry
;   r0  text
;   r1  string with key definition or 0 to ignore
;   r11  MenuBlock
;
; Exit
;   r0  text in string block
;------
;*******************************************************************************


TextAndKey	ROUTINE	"r1-r4"

		ADD	r4, r11, #MenuBlock_strings
		BL	StringCopy			; copy the leaf name into the string buffer
		BVS	error$l
		BL	MenuStringsRelocate
		MOV	r3, r0				; r3  menu text

		MOVS	r0, r1
		BEQ	nokey$l				; no key if 0 pointer
		LDRB	lr, [r1, #0]
		CMP	lr, #32
		BLT	nokey$l				; no key if null string

		BL	LookUpKey
		BVS	error$l
		MOV	r2, r0				; r2 = key number

		MOV	r0, r3				; keep r3 safe over relocation
		BL	strlen1
		MOV	r3, r0				; r3 = length of title string

		MOV	r0, r1
		ADD	r4, r11, #MenuBlock_strings
		BL	StringCopy			; if the key exists then write the string in
		BVS	error$l
		BL	MenuStringsRelocate
		MOV	lr, #' '
		STRB	lr, [r0, #-1]			; write a space over the terminator
		SUB	r3, r0, r3			; r3  menu text

		ADD	r4, r11, #MenuBlock_key_defs
		MOV	r0, #KeyBlock
		BL	StringCreate			; r0  KeyBlock
		BVS	error$l

		STR	r2, [r0, #KeyBlock_key]		; store hot key

		LDR	lr, [r11, #MenuBlock_entries]
		SUB	lr, lr, #1
		STR	lr, [r0, #KeyBlock_entry]	; and entry number

		LDR	lr, [r11, #MenuBlock_keys]	; one more key definition
		ADD	lr, lr, #1
		STR	lr, [r11, #MenuBlock_keys]
nokey$l
		MOV	r0, r3				; r0  string in block
		ClearV

error$l		EXIT


;*******************************************************************************
;----f- Director.s.Menus.MenuBlockFind
; Name
;   MenuBlockFind
;
; Purpose
;   This finds the given menu string in the menu list or returns a pointer
;   to the previous item
;
; Entry
;   r0  name of menu
;   r1  list anchor to search
;
; Exit
;   r0  menu block
;   r1  previous menu block (if found)
;   EQ means found
;   NE means not found
;------
;*******************************************************************************


MenuBlockFind	ROUTINE	"r2-r4", EXPORT

		MOV	r4, r1				; r4  list anchor

loop$l		ListWalk	r4, r2			; r2  previous, r4  current
		BEQ	notfound$l
		LDR	r1, [r4, #MenuBlock_name]
		BL	strcmpi				; to_find - current
		BGT	loop$l				; continue until alphabetical place found
		BNE	notfound$l

found$l		MOV	r0, r4				; point to current menublock
		MOV	r1, r2
		SetZ
		EXIT

notfound$l	MOV	r0, r4				; point to current menublock
		MOV	r1, r2
		ClearZ
		EXIT


;*******************************************************************************
;----f- Director.s.Menus.MenuBlockCreate
; Name
;   MenuBlockCreate
;
; Purpose
;   This creates a new menu block and links it into the end of the list supplied
;   It initialises all the fields in the menu block
;   It may return an error
;
; Entry
;   r0  menu name
;   r1  list anchor to link into
;   r2 = 0 for permanent, <>0 for temporary
;
; Exit
;   r0  menu block
;------
;*******************************************************************************


MenuBlockCreate	ROUTINE	"r1-r5", EXPORT

		MOV	r5, r2				; r5 is temporary status
		MOV	r2, r0				; r2  menu name
		MOV	r4, r1				; r4  list anchor


		MOV	r0, #MenuBlock
		BL	malloc
		EXIT	VS				; exit with flags on error

		MOV	r3, r0				; r3 is menu block
		MOV	r1, #MenuBlock
		BL	memclear			; clear the menu block to 0

		MOVS	r0, r2
		BEQ	noname$l
		BL	strdup
		EXIT	VS
noname$l	STR	r0, [r3, #MenuBlock_name]

		STR	r5, [r3, #MenuBlock_temporary]	; store temporary status

		Link	r3, r4, lr			; link the block into the end of the list

		MOV	r0, r3				; r0  menu block
		ClearV
		EXIT


;*******************************************************************************
;----f- Director.s.Menus.MenuBlockDestroy
; Name
;   MenuBlockDestroy
;
; Purpose
;   This destroys a menu block and unlinks it from the list
;It releases any memory attached also
;
; Entry
;   r0  menu block
;   r1  previous menu block
;
; Exit
;   none
;------
;*******************************************************************************


MenuBlockDestroy	ROUTINE	"r0-r3", EXPORT

		MOV	r3, r0				; r3  MenuBlock
		UnLink	r3, r1, lr			; unlink the MenuBlock

		LDR	r0, [r3, #MenuBlock_menu + StringBlock_buffer] ; free attached items
		BL	free
		LDR	r0, [r3, #MenuBlock_strings + StringBlock_buffer]
		BL	free
		LDR	r0, [r3, #MenuBlock_name]
		BL	free
		LDR	r0, [r3, #MenuBlock_key_defs]
		BL	free

		MOV	r0, r3				; free block
		BL	free

		EXIT


;*******************************************************************************
;----f- Director.s.Menus.PatchUpMenu
; Name
;   PatchUpMenu
;
; Purpose
;   This patches up the current menu, updating any path items to have the
;   correct sprites
;
; Entry
;   r11  menu block
;
; Exit
;   none
;------
;*******************************************************************************


PatchUpMenu	ROUTINE	"r0-r8"

		LDR	r8, [r11, #MenuBlock_menu]	; r8  wimp menu block
		ADD	r8, r8, #Wimp_Menu_entries	; r8  first entry

loop$l		LDR	r5, [r8, #Wimp_MenuEntry_icon_flags] ; r5 = Wimp_MenuEntry_icon_flags
		AND	r0, r5, #Wimp_IconESG
		MOV	r0, r0, LSR#Wimp_IconESGShift	; r0 = type of icon

		TEQ	r0, #MenuEntryPath
		TEQNE	r0, #MenuEntryPathUp
		TEQNE	r0, #MenuEntryPathMenuMenu
		BNE	next$l				; don't adjust unless it is a path

		LDR	r6, [r8, #Wimp_MenuEntry_data + Wimp_IconData_indirected_text_validation]

;;;; If this check is taken out then Director will check all path entries all of the time
		ADD	r0, r6, #1			; point to sprite in validation string
		ADR	r1, UnknownApp$l
		BL	strcmpi				; If small_app then reread
		BEQ	reread$l
		ADR	r1, UnknownFile$l
		BL	strcmpi				; if small_xxx then reread
		BNE	next$l

reread$l	MOV	r0, r6				; skip validation string
		BL	strlen1
		ADD	r0, r6, r0			; r0  path
		BL	PathToLeafAndType		; r1 = object type, r2 = load address, r3  leaf name
		CMP	r1, #OSFile_NotFound
		BICNE	r5, r5, #Wimp_IconShaded	; not shaded if item found
		ORREQ	r5, r5, #Wimp_IconShaded	; shade the item if not found
		MOV	r0, r6				; r0  validation
		MOV	r4, #0				; r4 = small icon
		BL	FileSprite
		CMP	r0, #0				; do we need to scale?
		BICEQ	r5, r5, #Wimp_IconHalfSize	; full size
		ORRNE	r5, r5, #Wimp_IconHalfSize	; make 1/2 size
		STR	r5, [r8, #Wimp_MenuEntry_icon_flags]

next$l		LDR	lr, [r8, #Wimp_MenuEntry_menu_flags]
		TST	lr, #Wimp_MenuLast		; Is it the last?
		ADDEQ	r8, r8, #Wimp_MenuEntry		; size of an entry
		BEQ	loop$l

		EXIT

UnknownFile$l	DCB	"small_xxx   ", 0
UnknownApp$l	DCB	"small_app   ", 0
		ALIGN


;*******************************************************************************
;----f- Director.s.Menus.MenuBlockForDisplay
; Name
;   MenuBlockForDisplay
;
; Purpose
;   This moves the block pointed to onto the displayed list
;   It may return an error
;
; Entry
;   r11  menuBlock
;
; Exit
;   none
;------
;*******************************************************************************


MenuBlockForDisplay	ROUTINE	"r0-r3", EXPORT

		LDR	r0, [r11, #MenuBlock_name]	; r0  name
		ADR	r1, MenuStoredAnchor
		BL	MenuBlockFind
		BNE	notfound$l

		UnLink	r0, r1, lr			; remove it from the list

		ADR	r1, MenuDisplayedAnchor
		ListEnd	r1, lr
		Link	r0, r1, lr			; and link it onto end of displayed list

		BL	PatchUpMenu

		ClearV
		EXIT

notfound$l	ADR	r0, notfoundmsg$l
		BL	MessageErrorLookup

error$l		SetV
		EXIT

notfoundmsg$l	ERROR	"$MenuNotFoundOrDisplayed", Error_MenuNotFoundOrDisplayed


;*******************************************************************************
;----f- Director.s.Menus.MenuBlockListClear
; Name
;   MenuBlockListClear
;
; Purpose
;   This clears the MenuBlock linked list from r0
;   If a menu block was not a temporary block then it is
;   unlinked and linked back into the CommandMenu list
;
; Entry
;   r0  list anchor
;
; Exit
;   none
;------
;*******************************************************************************


MenuBlockListClear	ROUTINE	"r0-r3", EXPORT

loop$l		ListWalkForDestruction	r0, r1, r2
		EXIT	EQ
		LDR	lr, [r0, #MenuBlock_temporary]
		CMP	lr, #0
		BEQ	dontdestroy$l
		BL	MenuBlockDestroy
		B	loop$l

dontdestroy$l	UnLink	r0, r1, lr			; remove from menu list

		STMFD	sp!, {r0-r2}

			MOV	r3, r0			; r3  MenuBlock

			LDR	r0, [r3, #MenuBlock_name]
			ADR	r1, MenuStoredAnchor
			BL	MenuBlockFind		; r0  found, r1  previous

			Link	r3, r1, lr		; add back to stored list

			MOVEQ	r0, r3
			BLEQ	MenuBlockDestroy	; remove the menu block if it was duplicated

		LDMFD	sp!, {r0-r2}

		B	loop$l
		EXIT


;*******************************************************************************
;----f- Director.s.Menus.MenuBlockListDestroy
; Name
;   MenuBlockListDestroy
;
; Purpose
;   This destroys all the menu blocks in the list provided
;
; Entry
;   r0  list anchor
;
; Exit
;   none
;------
;*******************************************************************************


MenuBlockListDestroy	ROUTINE	"r0-r3", EXPORT

loop$l		ListWalkForDestruction	r0, r1, r2
		BEQ	exitloop$l
		BL	MenuBlockDestroy
		B	loop$l

exitloop$l	EXIT


;*******************************************************************************
;----f- Director.s.Menus.StringCreate
; Name
;   StringCreate
;
; Purpose
;   This creates a string in the string block referenced
;   It may return an error
;
; Entry
;   r0 = length of string (including terminator)
;   r4  StringBlock
;
; Exit
;   r0  start of space for string
;------
;*******************************************************************************


StringCreate	ROUTINE	"r1-r5", EXPORT

		MOV	r5, r0				; string size
		LDR	r0, [r4, #StringBlock_buffer]	; start
		LDR	r2, [r4, #StringBlock_offset]	; current offset
		LDR	r3, [r4, #StringBlock_size]	; size

		SUB	lr, r3, r2			; remaining = size - offset
		CMP	lr, r5				; if remaining >= string size then ok
		BHS	ok$l

		STR	r0, [r4, #StringBlock_buffer_old] ; old start

		MOV	r1, #16				; grow the block
		CMP	r5, r1
		MOVHI	r1, r5				; take bigger of r1 and request
		ADD	r1, r1, #15
		BIC	r1, r1, #15			; round next higher multiple of 16
		BL	realloc
		BVS	error$l
		STR	r0, [r4, #StringBlock_buffer]	; new start
		ADD	r3, r3, r1			; size += r1
		STR	r3, [r4, #StringBlock_size]	; new size
ok$l
		ADD	r0, r0, r2			; r0  space for string
		ADD	r2, r2, r5			; current += string size
		STR	r2, [r4, #StringBlock_offset]	; increment current offset

		ClearV

error$l		EXIT


;*******************************************************************************
;----f- Director.s.Menus.StringMove
; Name
;   StringMove
;
; Purpose
;   This copies the memory pointed to into the string block
;   It may return an error
;
; Entry
;   r0  memory to copy
;   r1 = length of memory
;   r4  StringBlock
;
; Exit
;   r0  memory in string block
;------
;*******************************************************************************


StringMove	ROUTINE	"r1-r3", EXPORT

		MOV	r2, r0				; from
		MOV	r0, r1				; length of memory
		BL	StringCreate			; create it
		BVS	error$l
		MOV	r3, r0				; to (save pointer to string)
		MOV	r0, r2				; from
		MOV	r2, r1				; size
		MOV	r1, r3				; to
		BL	memcpy				; copy the string in
		MOV	r0, r3				; point to string in block

		ClearV

error$l		EXIT


;*******************************************************************************
;----f- Director.s.Menus.StringCopy
; Name
;   StringCopy
;
; Purpose
;   This copies the string pointed to into the string block
;
; Entry
;   r0  string to copy
;   r4  StringBlock
;
; Exit
;   r0  string in string block
;------
;*******************************************************************************


StringCopy	ROUTINE	"r1-r2", EXPORT

		MOV	r2, r0
		BL	strlen1				; length of string
		MOV	r1, r0				; length
		MOV	r0, r2				; from
		BL	StringMove

		EXIT					; return with error status


;*******************************************************************************
;----f- Director.s.Menus.StringCopyT
; Name
;   StringCopyT
;
; Purpose
;   This copies the string pointed to into the string block up to a
;   terminating character
;
; Entry
;   r0  string to copy
;   r4  StringBlock
;   r5 = terminating character
;
; Exit
;   r0  string in string block
;   It may return an error
;------
;*******************************************************************************


StringCopyT	ROUTINE	"r1-r2", EXPORT

		MOV	r2, r0
		BL	strlent0			; length of string
		MOV	r1, r0				; length
		MOV	r0, r2				; from
		BL	StringMove
		EXIT					; return with error status


;*******************************************************************************
;----f- Director.s.Menus.StringMark
; Name
;   StringMark
;
; Purpose
;   This puts a string mark in the string block
;   It may return an error
;
; Entry
;   r4  StringBlock
;
; Exit
;
;------
;*******************************************************************************


StringMark	ROUTINE	"r0", EXPORT

		MOV	r0, #1
		BL	StringCreate			; create a length 1 string
		BVS	error$l
		MOV	lr, #string_marker
		STRB	lr, [r0]			; write mark in

		EXIT

error$l		EXIT_ERR


;*******************************************************************************
;----f- Director.s.Menus.StringFindMark
; Name
;   StringFindMark
;
; Purpose
;   This finds the first string mark in the block from the pointer provided
;   to the start of the block
;   It may return an error
;
; Entry
;   r0  position in block
;   r4  StringBlock
;
; Exit
;   r0  marker in block
;------
;*******************************************************************************


StringFindMark	ROUTINE	"r1-r2", EXPORT

		LDR	r1, [r4, #StringBlock_buffer]	; start
		LDR	r2, [r4, #StringBlock_offset]	; current offset
		ADD	r2, r2, r1			; r2  current

		CMP	r0, r1				; check to see if pointer is in range
		BLE	range$l
		CMP	r0, r2
		BGT	range$l

loop$l		LDRB	lr, [r0, #-1]!
		CMP	lr, #string_marker
		BEQ	found$l
		CMP	r0, r1
		BGT	loop$l

		ADR	r0, nomarkermsg$l
		BL	MessageErrorLookup
		B	error$l

found$l		ClearV
		EXIT

range$l		ADR	r0, rangemsg$l
		BL	MessageErrorLookup

error$l		SetV
		EXIT

nomarkermsg$l	ERROR	"$NoMarker", Error_NoMarker
rangemsg$l	ERROR	"$NotInStringBlock", Error_NotInStringBlock


;*******************************************************************************
;----f- Director.s.Menus.MenuInitialise
; Name
;   MenuInitialise
;
; Purpose
;   This starts off a Menu
;   Allocates memory for the number of entries in the menublock
;   Sets up the title block of the menu
;   Makes a "Nothing" entry which will be over written by the first real entry
;   It may return an error
;
; Entry
;   r0  title for menu
;   r1  key string
;   r11  current MenuBlock
;
; Exit
;
;------
;*******************************************************************************


MenuInitialise	ROUTINE	"r0-r4, r8", EXPORT

		BL	TextAndKey
		BVS	error$l
		MOV	r3, r0				; r3  title

		ADD	r4, r11, #MenuBlock_menu
		MOV	r0, #Wimp_Menu
		BL	StringCreate			; make space for a new menu item
		BVS	error$l
		MOV	r8, r0				; r8  menu

		MOV	r0, #Wimp_ColourBlack
		STRB	r0, [r8, #Wimp_Menu_title_fg]

		MOV	r0, #Wimp_ColourLightGrey
		STRB	r0, [r8, #Wimp_Menu_title_bg]

		MOV	r0, #Wimp_ColourBlack
		STRB	r0, [r8, #Wimp_Menu_work_fg]

		MOV	r0, #Wimp_ColourWhite
		STRB	r0, [r8, #Wimp_Menu_work_bg]

		MOV	r0, #Wimp_MenuItemHeight
		STR	r0, [r8, #Wimp_Menu_height]

		MOV	r0, #Wimp_MenuItemGap
		STR	r0, [r8, #Wimp_Menu_gap]

		STR	r3, [r8, #Wimp_Menu_title_data_indirected_text_text] ; indirected title text
		MOV	r0, r3
		BL	strlen
		MOV	r0, r0, LSL#4			; width in OS units of title
		STR	r0, [r8, #Wimp_Menu_width]	; Adjust the start block for the menu width

		ADD	r8, r8, #Wimp_Menu_entries	; point to first menu block
		MOV	r0, #Wimp_Menu_entries
		STR	r0, [r11, #MenuBlock_menu + StringBlock_offset] ; adjust offset to point to first entry

		ADR	r0, NothingEntry
		MOV	r1, r8
		MOV	r2, #Wimp_MenuEntry
		BL	memcpy				; copy a null menu entry in

		ClearV
		EXIT

error$l		EXIT_ERR

NothingEntry	DCD	Wimp_MenuLast + Wimp_MenuTitleIndirected
		DCD	-1				; no sub menu
		DCD	Wimp_IconText + (Wimp_ColourBlack << Wimp_IconFGColourShift)+ (Wimp_ColourWhite << Wimp_IconBGColourShift) + Wimp_IconShaded + (MenuEntryText << Wimp_IconESGShift)
		DCB	"Nothing", 0
		ALIGN


;*******************************************************************************
;----f- Director.s.Menus.MenuItemStart
; Name
;   MenuItemStart
;
; Purpose
;   This makes a new item in the menu block
;   It may return an error
;
; Entry
;   r11  current MenuBlock
;
; Exit
;   r8  Wimp_MenuEntry
;------
;*******************************************************************************


MenuItemStart	ROUTINE	"r0-r4", EXPORT

		ADD	r4, r11, #MenuBlock_menu
		MOV	r0, #Wimp_MenuEntry
		BL	StringCreate			; make space for a new menu item
		BVS	error$l
		MOV	r8, r0

		LDR	lr, [r11, #MenuBlock_entries]
		ADD	lr, lr, #1
		STR	lr, [r11, #MenuBlock_entries]	; show 1 more entry

		STR	r8, CurrentMenuItem

		EXIT

error$l		EXIT_ERR


;*******************************************************************************
;----f- Director.s.Menus.MenuItemEnd
; Name
;   MenuItemEnd
;
; Purpose
;   This gets the vital statistics of a menu item and and adds them into
;   the menu so far, keeping the menu up to date.
;   It also writes the size of the indirected item
;
; Entry
;   r8  Wimp_MenuEntry
;   r11  current MenuBlock
;
; Exit
;   none
;------
;*******************************************************************************


MenuItemEnd	ROUTINE	"r0-r4", EXPORT

		LDR	r4, [r11, #MenuBlock_menu + StringBlock_buffer] ; r4  start of menu

		LDR	r0, [r8, #Wimp_MenuEntry_data + Wimp_IconData_indirected_text_text]
		BL	strlen
		ADD	lr, r0, #1
		STR	lr, [r8, #Wimp_MenuEntry_data + Wimp_IconData_indirected_text_size]

		MOV	r0, r0, LSL#4
		ADD	r0, r0, #16			; width in OS units of menu text

		LDR	lr, [r8, #Wimp_MenuEntry_icon_flags]
		TST	lr, #Wimp_IconSprite
		ADDNE	r0, r0, #32			; add in size of sprite if it exists

		LDR	lr, [r4, #Wimp_Menu_width]
		CMP	r0, lr
		STRGT	r0, [r4, #Wimp_Menu_width]	; Adjust the start block for the menu width

		LDR	r0, [r11, #MenuBlock_height]
		ADD	r0, r0, #Wimp_MenuItemHeight + Wimp_MenuItemGap ; add in height of entry
		LDR	lr, [r8, #Wimp_MenuEntry_menu_flags]
		TST	lr, #Wimp_MenuSeparate
		ADDNE	r0, r0, #Wimp_MenuItemSeparation ; and of separator if there is one
		STR	r0, [r11, #MenuBlock_height]

		LDR	lr, [r11, #MenuBlock_entries]
		CMP	lr, #1				; is this the first entry?

		LDR	lr, [r8, #Wimp_MenuEntry_menu_flags]
		ORREQ	lr, lr, #Wimp_MenuTitleIndirected ; Adjust the first entry to show that the title is indirect
		ORR	lr, lr, #Wimp_MenuLast		; Adjust the entry to show it is the last
		STR	lr, [r8, #Wimp_MenuEntry_menu_flags]

		LDRNE	lr, [r8, #-Wimp_MenuEntry + Wimp_MenuEntry_menu_flags]
		BICNE	lr, lr, #Wimp_MenuLast		; Adjust previous entry to show it is not the last
		STRNE	lr, [r8, #-Wimp_MenuEntry + Wimp_MenuEntry_menu_flags]

		EXIT


;*******************************************************************************
;----f- Director.s.Menus.MenuStringsRelocate
; Name
;   MenuStringsRelocate
;
; Purpose
;   This sees whether the string block has changed position, and if so
;   it relocates all the pointers into it
;
; Entry
;   r11  current MenuBlock
;
; Exit
;
;------
;*******************************************************************************


		MACRO
$label		Reloc	$reg, $offset
$label		LDR	r0, $reg, $offset
		CMP	r0, r2				; if ptr >= start
		CMPHS	r3, r0				; and end - 1 >= ptr
		ADDHS	r0, r0, r1			; then relocate pointer by r1
		STRHS	r0, $reg, $offset		; and store
		MEND


MenuStringsRelocate	ROUTINE	"r0-r6, r8", EXPORT

		ADD	r4, r11, #MenuBlock_strings

		LDR	r1, [r4, #StringBlock_buffer]
		LDR	r2, [r4, #StringBlock_buffer_old]
		SUBS	r1, r1, r2			; r1 is relocation change
		CMPNE	r2, #0
		EXIT	EQ				; exit if old pointer is 0 or old pointer = new pointer

		LDR	r3, [r4, #StringBlock_offset]
		ADD	r3, r3, r2			; r3 points to end of old block
		SUB	r3, r3, #1			; end - 1

		LDR	r8, [r11, #MenuBlock_menu + StringBlock_buffer] ; point to menu block
		CMP	r8, #0
		BEQ	end$l

		Reloc	[r8, #Wimp_Menu_title_data_indirected_text_text]

		LDR	r5, [r11, #MenuBlock_menu + StringBlock_offset] ; current offset
		ADD	r5, r8, r5			; r5  end of menu block
		SUB	r5, r5, #Wimp_MenuEntry		; r5  last MenuEntry in the block

		ADD	r8, r8, #Wimp_Menu_entries	; r8  first entry

loop$l		CMP	r8, r5
		BHI	end$l				; end if we step over the last entry
		Reloc	[r8, #Wimp_MenuEntry_data + Wimp_IconData_indirected_text_text]
		Reloc	[r8, #Wimp_MenuEntry_data + Wimp_IconData_indirected_text_validation]
		ADD	r8, r8, #Wimp_MenuEntry
		B	loop$l

end$l		LDR	lr, [r4, #StringBlock_buffer]
		STR	lr, [r4, #StringBlock_buffer_old] ; relocate no more

		EXIT


;*******************************************************************************
;----f- Director.s.Menus.SpriteExists
; Name
;   SpriteExists
;
; Purpose
;   This sees whether the sprite  r0 exists
;
; Entry
;   r0  sprite name
;
; Exit
;   VS sprite not ok
;   VC sprite ok
;------
;*******************************************************************************


SpriteExists	ROUTINE	"r0-r3"

		MOV	r2, r0
		MOV	r0, #WimpSpriteOp_SelectSprite
		SWI	XWimp_SpriteOp

		EXIT					; return flags


;*******************************************************************************
;----f- Director.s.Menus.WriteXXX
; Name
;   WriteXXX
;
; Purpose
;   This writes r0 as 3 hex digits at r1 which it then null terminates
;
; Entry
;   r0 = number
;   r1  string
;
; Exit
;
;------
;*******************************************************************************


WriteXXX	ROUTINE	"r0-r3"

		MOV	r3, #3				; nibbles
		MOV	r0, r0, LSL #32-3*4		; put ms nibble at top
loop$l		MOV	r2, r0, LSR #32-4		; get top nibble
		CMP	r2, #10
		ADDLT	r2, r2, #'0'
		ADDGE	r2, r2, #'A'-10
		STRB	r2, [r1], #1
		MOV	r0, r0, LSL#4			; next nibble please
		SUBS	r3, r3, #1
		BGT	loop$l

		MOV	lr, #0
		STRB	lr, [r1], #1			; 0 terminate

		EXIT


;*******************************************************************************
;----f- Director.s.Menus.FileSprite
; Name
;   FileSprite
;
; Purpose
;   This makes a sprite from a file name and type
;   Validation string is 's' + 12 chars for sprite name + '\0' = 14 characters maximum
;   Sprite names are padded with ; to 12 characters
;
; Entry
;   r0  place for validation string
;   r1 = object type
;   r2 = file type
;   r3  leaf of file
;   r4 = 0 for small icon, 1 for large
;
; Exit
;   r0 => sprite needs scaling
;------
;*******************************************************************************


FileSprite	ROUTINE	"r1-r9", EXPORT

		MOV	r9, #0				; sprite doesn't need scaling
		MOV	r8, r0				; r8  place for validation string
		MOV	r7, r1				; r7 = object type
		MOV	r6, r2				; r6 = file type
		MOV	r5, r3				; r5  leaf

		MOV	r0, #'s'
		STRB	r0, [r8], #1			; store initial validation character

		CMP	r7, #OSFile_NotFound
		CMPNE	r6, #OSFile_TypeUntyped
		BEQ	untyped$l

		CMP	r7, #OSFile_IsFile
		BEQ	file$l

		CMP	r6, #OSFile_TypeApplication
		BEQ	application$l

		CMP	r7, #OSFile_IsImage
		BEQ	file$l

		CMP	r7, #OSFile_IsDir
		BEQ	dir$l

file$l		CMP	r4, #0
		ADREQ	r0, SmallFile
		ADRNE	r0, LargeFile
		MOV	r1, r8
		BL	strcpy
		SUB	r1, r1, #1			; write over terminator

		MOV	r0, r6
		BL	WriteXXX

		MOV	r0, r8				; point to sprite
		BL	SpriteExists
		BVC	done$l				; sprite existed, ok

		CMP	r4, #0
		BNE	untyped$l			; end if large file not found

		ADR	r0, LargeFile
		MOV	r1, r8
		BL	strcpy
		SUB	r1, r1, #1			; write over terminator

		MOV	r0, r6
		BL	WriteXXX

		MOV	r0, r8				; point to sprite
		BL	SpriteExists
		MOVVC	r9, #1				; does need scaling
		BVC	done$l				; sprite existed, ok

		B	untyped$l

SmallFile	DCB	"small_", 0
LargeFile	DCB	"file_", 0
SmallDir	DCB	"small_dir", 0
LargeDir	DCB	"directory", 0
SmallApp	DCB	"sm", 0
;SmallUnknown is moved below
;SmallUnknown	DCB	"small_xxx", 0
LargeUnknown	DCB	"file_xxx", 0
SmallUnknownApp	DCB	"small_app", 0
LargeUnknownApp	DCB	"application", 0
		ALIGN

application$l	MOV	r1, r8
		CMP	r4, #0
		BNE	no_sm$l
		ADR	r0, SmallApp			; write an sm if we are small
		BL	strcpy
		SUB	r1, r1, #1			; write over terminator
		MOV	r2, #10				; 10 characters only (sm...)
		MOV	r0, r5				; !name
		BL	memcpy
		MOV	lr, #0
		STRB	lr, [r1, #10]			; null terminate extreme end
		MOV	r0, r8				; point to sprite
		BL	SpriteExists
		BVC	done$l

		; We have to try to find a large sprite to scale down:
		MOV	r9, #1

no_sm$l		MOV	r1, r8
		MOV	r0, r5				; !name
		MOVNE	r2, #12				; 12 characters only (...)
		BL	memcpy
		MOV	lr, #0
		STRB	lr, [r1, #12]			; null terminate extreme end

		MOV	r0, r8				; point to sprite
		BL	SpriteExists
		BVC	done$l				; sprite existed, ok

		; Ensure we're not going to scale any subsequent icon. 
		MOV	r9, #0

untypedapp$l	CMP	r4, #0
		ADREQ	r0, SmallUnknownApp
		ADRNE	r0, LargeUnknownApp
		B	copyanddone$l

dir$l		CMP	r4, #0
		ADREQ	r0, SmallDir
		ADRNE	r0, LargeDir
		B	copyanddone$l

untyped$l	CMP	r4, #0
		ADREQ	r0, SmallUnknown
		ADRNE	r0, LargeUnknown
copyanddone$l
		MOV	r1, r8
		BL	strcpy

done$l		MOV	r0, #0
		STRB	r0, [r8, #12]			; 0 terminate
		MOV	r0, r8
		BL	strlen				; r0 = length of string
		RSBS	r1, r0, #12			; 12 - length, ie length to fill
		ADD	r0, r8, r0			; place to start fill from
		MOV	r2, #' '
		BLGE	memset				; pad sprite name with ' '

		MOV	r0, r9
		EXIT


;*******************************************************************************
;----f- Director.s.Menus.MenuAddFileItem
; Name
;   MenuAddFileItem
;
; Purpose
;   This adds a file item into the current menu
;   If file type is not found then it adds the file in greyed out
;   It may return an error
;
; Entry
;   r0  menu text
;   r1 = object type
;   r2 = file type, -2 for no sprite
;   r3  leaf of file
;   r4  key string
;   r11  current MenuBlock
;
; Exit
;
;------
;*******************************************************************************


MenuAddFileItem	ROUTINE_SF	"r0-r9", EXPORT
validation$l	#	32
		END_SF

		MOV	r7, r1				; r7 = object type
		MOV	r6, r2				; r6 = file type
		MOV	r5, r3				; r5  leaf

		BL	MenuItemStart
		BVS	error$l

		MOV	r1, r4
		BL	TextAndKey			; convert entry text and
		BVS	error$l
		STR	r0, [r8, #Wimp_MenuEntry_data + Wimp_IconData_indirected_text_text] ; pointer to name

		CMP	r6, #-2
		ADR	r0, validation$l		; space for validation
		MOV	r1, r7				; r1 = object type
		MOV	r2, r6				; r2 = file type
		MOV	r3, r5				; r3  leaf
		MOV	r4, #0				; small icon please
		STREQB	r4, [r0]			; make sure the validation is null if unused
		BEQ	nosprite2$l
		BL	FileSprite
		MOV	r4, r0				; r4 = needs scaling?
nosprite2$l

		CMP	r7, #OSFile_IsDir
		CMPNE	r7, #OSFile_IsImage

		MOV	r0, #Wimp_MenuGiveWarning
		STR	r0, [r8, #Wimp_MenuEntry_menu_flags]

		MOVEQ	r0, #1				; show we need to make a sub-menu
		MOVNE	r0, #-1				; no sub-menu
		STR	r0, [r8, #Wimp_MenuEntry_sub_menu]

		LDR	r0, =Wimp_IconText + (Wimp_ColourBlack << Wimp_IconFGColourShift)+ (Wimp_ColourWhite << Wimp_IconBGColourShift) + Wimp_IconIndirected + (MenuEntryFile << Wimp_IconESGShift) + Wimp_IconSprite + Wimp_IconVCentred
		CMP	r6, #-2
		BICEQ	r0, r0, #Wimp_IconSprite	; show no sprite
		BEQ	nosprite$l
;;;;		CMP	r7, #OSFile_NotFound
;;;;		ORREQ	r0, r0, #Wimp_IconShaded	; shade the item if not found
		CMP	r4, #0
		ORRNE	r0, r0, #Wimp_IconHalfSize	; make 1/2 size
nosprite$l
		STR	r0, [r8, #Wimp_MenuEntry_icon_flags]

		ADR	r0, validation$l
		ADD	r4, r11, #MenuBlock_strings
		BL	StringCopy
		BVS	error$l
		BL	MenuStringsRelocate
		STR	r0, [r8, #Wimp_MenuEntry_data + Wimp_IconData_indirected_text_validation]

		BL	MenuItemEnd

		ClearV
		EXIT

error$l		EXIT_ERR

SmallUnknown	DCB	"small_xxx", 0
		ALIGN


;*******************************************************************************
;----f- Director.s.Menus.LoadAddrToType
; Name
;   LoadAddrToType
;
; Purpose
;   This converts a leaf name, object type an load address into a file type
;   We use this in preference to the OS routines because the OS routines
;   return the file type of an image as a directory which is not what we want
;   for displaying the icon
;
; Entry
;   r1 = object type
;   r2 = load address
;   r3  leaf name
;
; Exit
;   r2 = file type
;------
;*******************************************************************************


LoadAddrToType	ROUTINE "", EXPORT

		CMP	r1, #OSFile_NotFound		; exit if file not found
		MOVEQ	r2, #OSFile_TypeUntyped
		EXIT	EQ

		CMP	r1, #OSFile_IsDir		; directories have no type
		MOVEQ	r2, #OSFile_TypeDir
		BEQ	is_dir$l

		MOV	lr, r2, LSR#20
		EOR	lr, lr, #&0FF
		EORS	lr, lr, #&F00
		MOVNE	r2, #OSFile_TypeUntyped
		EXIT	NE

		MOV	r2, r2, LSL#12
		MOV	r2, r2, LSR#20			; r2 = file type

		CMP	r1, #OSFile_IsFile
		EXIT	EQ				; exit if not image or directory

is_dir$l	LDRB	lr, [r3]			; get initial character of leaf name
		CMP	lr, #'!'
		MOVEQ	r2, #OSFile_TypeApplication

		EXIT


;*******************************************************************************
;----f- Director.s.Menus.PathToLeafAndType
; Name
;   PathToLeafAndType
;
; Purpose
;    takes a path name and returns the leaf and the type of the path
;
; Entry
;   r0  path name
;
; Exit
;   r1 = type
;   r2 = OSFile_Type*
;   r3  leaf
;------
;*******************************************************************************


PathToLeafAndType	ROUTINE	"r0,r4-r7", EXPORT

		MOV	r7, r0				; r7  path
		BL	PathParse
		MOV	r6, r0				; r6  leaf

		MOV	r0, #OSFile_ReadNoPath		; 17
		MOV	r1, r7
		BL	ClaimMediaUpCalls
		SWI	XOS_File
		MOV	r4, pc				; 26-bit: store flags in r4 (uses PSR)
		MRS	r4, CPSR			; 32-bit: store flags in r4 (uses PSR)
		BL	ReleaseMediaUpCalls
		TST	r4, #V_bit			; if the SWI generated an error
		MOVNE	r1, #OSFile_NotFound		; then set not found

		MOVEQ	r1, r0				; r1 = type
		MOV	r3, r6				; r3  leaf
		BL	LoadAddrToType			; r2 = OSFile_Type*

		EXIT


;*******************************************************************************
;----f- Director.s.Menus.MenuAddPathItem
; Name
;   MenuAddPathItem
;
; Purpose
;   This adds a path item into the current menu
;   It may return an error
;
; Entry
;   r0  path name
;   r1  menu text, 0 => use leaf name of path
;   r2  key string
;   r3 = flags
;     Bit 0:	sub-menu will produce upmenus
;     Bit 1:	OSFile_* type in r0
;     Bit 2:	OSFile_NotFound
;     Bit 3:	OSFile_TypeApplication
;     Bit 4:	OSFile_TypeDir || OSFile_IsImage
;     Bit 5:	OSFile_IsFile
;     Bit 6:  1 => don't add a sprite
;     Bit 7:  1 => open MenuMenu rather than path
;   r11  current MenuBlock
;
; Exit
;   r0 = OSFile_* type of file (if bit 1 set in r3)
;------
;*******************************************************************************


MenuAddPathItem	ROUTINE	"r0-r10", EXPORT

		MOV	r7, r0				; r7  path
		MOV	r9, r1				; r9  menu text
		MOV	r8, r2				; r8  key string

		BL	PathToLeafAndType
		MOV	r10, r3				; r10  leaf

		LDRSP	lr, r3				; lr = flags
		TST	lr, #1 << 1			; return OSFile_* type in r0
		STRSP	r1, r0, NE			; yes

		MVN	lr, lr				; lr = inverted flags (to make the tests easier)

		TST	lr, #1 << 2			; Not found
		TEQEQ	r1, #OSFile_NotFound
		BEQ	exit$l				; end if not found files not wanted

		TST	lr, #1 << 3			; applications
		TEQEQ	r2, #OSFile_TypeApplication
		BEQ	exit$l				; end if applications not wanted

		TEQ	r1, #OSFile_IsImage
		TEQNE	r2, #OSFile_TypeDir
		TSTEQ	lr, #1 << 4			; OSFile_TypeDir || OSFile_IsImage
		BEQ	exit$l				; end if directories not wanted

		TEQ	r1, #OSFile_IsFile
		TSTEQ	lr, #1 << 5			; OSFile_IsFile
		BEQ	exit$l				; end if files not wanted

;

		MOVS	r0, r9				; if  text = 0 then use leaf name
		BNE	notleaf$l
		MOV	r0, r10				; r0  leaf
		LDRB	lr, [r0, #0]
		CMP	lr, #'!'
		BNE	leafdone$l			; start of leaf is '!'
		LDRB	lr, [r0, #1]
		CMP	lr, #32				; and it is >1 character long
		ADDGE	r0, r0, #1			; so skip initial '!' character
leafdone$l
notleaf$l 	LDRSP	lr, r3				; lr = flags
		TST	lr, #1<<6			; should we be showing a sprite?
		MOVNE	r2, #-2				; show no sprite

		MOV	r4, r8				; r4  key string
		BL	MenuAddFileItem
		BVS	error$l

		LDR	r8, CurrentMenuItem
		LDR	r0, [r8, #Wimp_MenuEntry_icon_flags]
		BIC	r0, r0, #Wimp_IconESG

		LDRSP	lr, r3
		TST	lr, #1 << 7			; menu menu instead of path?
		ORRNE	r0, r0, #MenuEntryPathMenuMenu << Wimp_IconESGShift
		BNE	was_menumenu$l
		TST	lr, #1 << 0
		ORREQ	r0, r0, #MenuEntryPath << Wimp_IconESGShift
		ORRNE	r0, r0, #MenuEntryPathUp << Wimp_IconESGShift
was_menumenu$l
		STR	r0, [r8, #Wimp_MenuEntry_icon_flags] ; show that this item is a Path
		MOVNE	lr, #1
		STRNE	lr, [r8, #Wimp_MenuEntry_sub_menu] ; if upmenus, make sub-menu

		ADD	r4, r11, #MenuBlock_strings
		MOV	r0, r7
		BL	StringCopy			; put the path in after the validation string
		BVS	error$l
		BL	MenuStringsRelocate

exit$l		ClearV
		EXIT

error$l		EXIT_ERR


;*******************************************************************************
;----f- Director.s.Menus.MenuAddTextItem
; Name
;   MenuAddTextItem
;
; Purpose
;   This adds a text item into the current menu
;   It may return an error
;
; Entry
;   r0  text
;   r1 = length of block, or 0 to read it from string
;   r2  key string
;   r3  sprite or 0
;   r5  allowable characters or 0
;   r11  current MenuBlock
;
; Exit
;
;------
;*******************************************************************************


MenuAddTextItem	ROUTINE	"r0-r5, r8", EXPORT

		BL	MenuItemStart
		BVS	error$l

		MOV	r7, r1				; r7 is length of buffer or 0 for string size

		MOV	r1, r2
		BL	TextAndKey
		BVS	error$l
		STR	r0, [r8, #Wimp_MenuEntry_data + Wimp_IconData_indirected_text_text] ; pointer to name
		BL	strlen1
		SUBS	r0, r7, r0			; r0 = extension length
		MOVLE	r7, #0				; show buffer is big enough
		BLE	bufferok$l			; skip extension if buffer big enough
buffer$l	ADD	r4, r11, #MenuBlock_strings
		BL	StringCreate			; extend the buffer
		BVS	error$l
		BL	MenuStringsRelocate
bufferok$l
		MOV	r0, #0
		STR	r0, [r8, #Wimp_MenuEntry_menu_flags]

		MOV	r0, #-1				; no sub-menu
		STR	r0, [r8, #Wimp_MenuEntry_sub_menu]

		LDR	r0, =Wimp_IconText + (Wimp_ColourBlack << Wimp_IconFGColourShift)+ (Wimp_ColourWhite << Wimp_IconBGColourShift) + Wimp_IconIndirected + (MenuEntryText << Wimp_IconESGShift) + Wimp_IconVCentred

		CMP	r3, #0
		ORRNE	r0, r0, #Wimp_IconSprite	; show we have a sprite
		STR	r0, [r8, #Wimp_MenuEntry_icon_flags]

		ADD	r4, r11, #MenuBlock_strings	; point to current menu block
		CMP	r3, #0
		BEQ	no_sprite$l

		ADR	r0, SValidation			; Start of a sprite
		MOV	r1, #1				; length
		BL	StringMove			; copy the text into the string buffer
		BVS	error$l
		BL	MenuStringsRelocate
		STR	r0, [r8, #Wimp_MenuEntry_data + Wimp_IconData_indirected_text_validation]

		MOV	r0, r3
		BL	SpriteExists
		ADRVS	r0, SmallUnknown		; sprite name if not found
		MOVVC	r0, r3
		BL	StringCopy			; copy the sprite name in
		BVS	error$l
		BL	MenuStringsRelocate
		B	no_allow$l
no_sprite$l

		CMP	r5, #0
		ADREQ	r0, NullValidation		; No validation
		ADRNE	r0, AValidation			; Start of allowable characters
		MOV	r1, #1				; length
		BL	StringMove			; copy the text into the string buffer
		BVS	error$l
		BL	MenuStringsRelocate
		STR	r0, [r8, #Wimp_MenuEntry_data + Wimp_IconData_indirected_text_validation]
		CMP	r5, #0
		BEQ	no_allow$l

		MOV	r0, r5
;		MOV	r5, #";"
		BL	StringCopy			; copy the (un)allowable characters in
		BVS	error$l
		BL	MenuStringsRelocate
no_allow$l

		BL	MenuItemEnd
		CMP	r7, #0
		STRNE	r7, [r8, #Wimp_MenuEntry_data + Wimp_IconData_indirected_text_size] ; write proper size

		ClearV
		EXIT

error$l		EXIT_ERR

SValidation	DCB	"S"
NullValidation	DCB	0
AValidation	DCB	"A"
		ALIGN


;*******************************************************************************
;----f- Director.s.Menus.MenuAddGreyItem
; Name
;   MenuAddGreyItem
;
; Purpose
;   This adds a grey text item into the current menu
;   It may return an error
;
; Entry
;   r0  text
;   r11  current MenuBlock
;
; Exit
;
;------
;*******************************************************************************


MenuAddGreyItem	ROUTINE	"r0-r4,r8", EXPORT

		MOV	r1, #0				; read length of string
		MOV	r2, #0				; no key string
		MOV	r3, #0				; no sprite
		MOV	r5, #0				; no allowable characters
		BL	MenuAddTextItem
		BVS	error$l

		LDR	r8, CurrentMenuItem
		LDR	r0, [r8, #Wimp_MenuEntry_icon_flags]
		ORR	r0, r0, #Wimp_IconShaded
		STR	r0, [r8, #Wimp_MenuEntry_icon_flags] ; shade the entry

		EXIT

error$l		EXIT_ERR


;*******************************************************************************
;----f- Director.s.Menus.MenuUnderlineItem
; Name
;   MenuUnderlineItem
;
; Purpose
;   This underlines the last menu item
;
; Entry
;   r11  current MenuBlock
;
; Exit
;
;------
;*******************************************************************************


MenuUnderlineItem	ROUTINE	"r0-r4,r8", EXPORT

		LDR	r8, CurrentMenuItem
		LDR	r0, [r8, #Wimp_MenuEntry_menu_flags]
		ORR	r1, r0, #Wimp_MenuSeparate
		STR	r1, [r8, #Wimp_MenuEntry_menu_flags]

		CMP	r0, r1
		LDRNE	r0, [r11, #MenuBlock_height]
		ADDNE	r0, r0, #Wimp_MenuItemSeparation ; add in height of separator
		STRNE	r0, [r11, #MenuBlock_height]

		EXIT


;*******************************************************************************
;----f- Director.s.Menus.FileMenuUp
; Name
;   FileMenuUp
;
; Purpose
;   This creates all the up directories for a given path
;   It may return an error
;
; Entry
;   r0  path name
;   r11  MenuBlock
;
; Exit
;
;------
;*******************************************************************************


FileMenuUp	ROUTINE	"r0-r4", EXPORT

		MOV	r3, r0				; r3  path

		BL	PathParse			; get pointer to menu title
		MOV	r4, r0				; r4  leaf
		BEQ	norecurse$l			; no further down if no '.'

		LDRB	lr, [r0, #-1]			; what was terminator?
		CMP	lr, #"."
		SUBEQ	r2, r0, #1			; 0 terminate path (over write '.')
		MOVNE	r2, r0				; 0 terminate path (write past ':')

		LDRB	r1, [r2]			; r1 = contents of terminator location
		MOV	lr, #0				; write a null over the '.'
		STRB	lr, [r2]

		MOV	r0, r3
		BL	FileMenuUp			; recurse down a level
		BVS	error$l

		STRB	r1, [r2]			; restore the terminator

norecurse$l	MOV	r0, r3
		MOV	r1, r4				; use leaf name as text
		MOV	r2, #0				; no key string
		MOV	r3, #2_00			; no upmenus
		BL	MenuAddPathItem
		BVS	error$l

		EXIT

error$l		EXIT_ERR


;*******************************************************************************
;----f- Director.s.Menus.FileMenuCreate
; Name
;   FileMenuCreate
;
; Purpose
;   This makes a menu from a directory path
;   It may return an error
;
; Entry
;   r0  path name
;   r1 = 0 for no up directories, 1 for up directories
;
; Exit
;   r0  menu block
;------
;*******************************************************************************


FileMenuCreate	ROUTINE_SF	"r1-r11", EXPORT
info_size$l	EQU	4096
info_ptr$l	#	4
start_sort$l	#	4
		END_SF

		MOV	lr, #0
		STR	lr, info_ptr$l			; zero info_pointer

		MOV	r8, r0				; r8 is pointer to path
		MOV	r7, r1				; up directories?

		MOV	r0, r8
		BL	SetCurrentPath			; set variable for later
		BVS	error$l

		MOV	r0, #info_size$l + 512
		BL	malloc				; malloc a block for the GBPBs
		BVS	error$l
		STR	r0, info_ptr$l

		ADD	r1, r0, #info_size$l + 256	; bit of dead space on the end
		MOV	r0, r8
		MOV	r8, r1				; r8  string on the end of the block
		BL	strcpy				; copy the path string into the end of block

		MOV	r0, #0				; auto menu, not named
		ADR	r1, MenuDisplayedAnchor		; list to link into
		MOV	r2, #1				; temporary menu
		ListEnd	r1, lr				; end of the list
		BL	MenuBlockCreate			; create a new menu block to r0
		BVS	error$l
		MOV	r11, r0				; r11 points to menu block

		MOV	r0, r8
		BL	PathParse			; get pointer to menu title
		MOV	r1, #0				; no key string
		BL	MenuInitialise			; start the menu
		BVS	error$l

		CMP	r7, #0				; up directories?
		BEQ	noup$l
		MOV	r0, r8
		BL	FileMenuUp			; create all the up directories
		BVS	error$l
		BL	MenuUnderlineItem		; and underline the last one

		LDR	r0, CurrentMenuItem
		MOV	lr, #1
		STR	lr, [r0, #Wimp_MenuEntry_sub_menu] ; make sure last item has a sub-menu
		LDR	lr, [r0, #Wimp_MenuEntry_icon_flags]
		BIC	lr, lr, #Wimp_IconESG
		ORRNE	lr, lr, #MenuEntryPathMenuMenu << Wimp_IconESGShift
		STR	lr, [r0, #Wimp_MenuEntry_icon_flags] ; turn last item into menumenu

noup$l

		LDR	r0, [r11, #MenuBlock_entries]
		STR	r0, start_sort$l		; menu entry to start sorting from

		MOV	r0, r8
		BL	IfFileMakeUp			; make a director from the file
		BVS	empty$l				; empty if error

ok$l		ADD	r4, r11, #MenuBlock_strings
		BL	StringMark			; mark so we can find the path again
		BVS	error$l
		BL	MenuStringsRelocate
		MOV	r0, r8
		ADD	r4, r11, #MenuBlock_strings
		BL	StringCopy			; copy the path in
		BVS	error$l
		BL	MenuStringsRelocate

		MOV	r10, #0				; r10 is iterator
		MOV	r9, #0				; number of items added
loop$l
		MOV	r0, #OSGBPB_DirEntriesInfo	; 10
		MOV	r1, r8				; directory name
		LDR	r2, info_ptr$l			; buffer to read to
		MOV	r3, #256			; items to read
		MOV	r4, r10				; iterator
		MOV	r5, #info_size$l		; space in buffer
		MOV	r6, #0				; get all entries
		BL	ClaimMediaUpCalls
		SWI	XOS_GBPB
		MOV	r6, pc				; 26-bit: store flags in r6 (uses PSR)
		MRS	r6, CPSR			; 32-bit: store flags in r6 (uses PSR)
		BL	ReleaseMediaUpCalls
		TST	r6, #V_bit			; if the SWI generated an error
		BNE	empty$l
		MOV	r10, r4				; iterator

		LDR	r6, info_ptr$l			; r6  current entry
		MOVS	r5, r3				; r5 = number of items
		BEQ	noitems$l
loop2$l
		ADD	r9, r9, #1			; another item added
		LDR	r1, [r6, #OSGBPB_Info_obj_type]	; object type
		LDR	r2, [r6, #OSGBPB_Info_load_addr]
		ADD	r6, r6, #OSGBPB_Info_name	; r6  string
		MOV	r0, r6				;  leaf for menu text
		MOV	r3, r0				;  leaf
		BL	LoadAddrToType
		MOV	r4, #0				; no key
		BL	MenuAddFileItem
		BVS	error$l
		MOV	r0, r6
		BL	strlen1
		ADD	r6, r6, r0			; point to end of string
		ADD	r6, r6, #3			; add terminator and align
		BIC	r6, r6, #3

		SUBS	r5, r5, #1
		BGT	loop2$l
noitems$l
		CMP	r4, #-1
		BNE	loop$l				; end if all read

empty$l
		CMP	r9, #0				; if no entries added
		BGT	gotsome$l
		ADR	r0, EmptyMessage
		BL	MenuAddGreyItem			; make an empty menu
		BVS	error$l
gotsome$l

		LDR	r0, info_ptr$l
		BL	free				; free the GBPB block

		LDR	r0, start_sort$l
		LDR	r1, [r11, #MenuBlock_entries]
		SUB	r1, r1, #1
		BL	SortMenuBlock			; sort the menu

		MOV	r0, r11

		ClearV
		EXIT

error$l		MOV	r1, r0				; preserve r0
		LDR	r0, info_ptr$l
		BL	free				; free the GBPB block
		MOV	r0, r1
		SetV
		EXIT

EmptyMessage	EQUS0A	"Nothing"


;*******************************************************************************

		END
